#include "basicparser.h"
#include "errors.h"
#include <map>

namespace CCompiler
{
    std::set<TokenType> OpGroups[lowest_level + 1];
    std::set<TokenType> UnaryOps;
    std::set<TokenType> AssignOps;
    std::map<std::string, std::string> AssignOp2BinOp;

    template<int priority>
    SynExpression *BasicParser::internalParseExpression(Int2Type<priority>)
    {
        SynExpression *res = internalParseExpression(Int2Type<priority + 1>());
        do
        {
            if(OpGroups[priority].count(getTokenType()))
            {
                std::string name = tok_.getText();
                nextToken();
                res = new SynBinaryOp(name, res, internalParseExpression(Int2Type<priority + 1>()));
            }
            else break;
        } while (true);
        return res;
    }

    SynExpression *BasicParser::parseIdentifier()
    {
        SynExpression* res = new SynVal(tok_.getText());
        nextToken();
        return res;
    }

    SynExpression *BasicParser::internalParseExpression(Int2Type<lowest_level>)
    {
        SynExpression *res = NULL;
        switch(getTokenType())
        {
        case ttOpenParenthesis:
            nextToken();
            res = parseExpression();
            if(getTokenType() != ttCloseParenthesis)
                throw Errors::SyntaxError("Parenthesis mismatch", tok_);
            break;
        case ttInteger:
            res = new SynInt(tok_.getIntValue()); break;
        case ttChar:
            res = new SynChar(tok_.getCharValue()); break;
        case ttString:
            res = new SynString(tok_.getStringValue()); break;
        case ttReal:
            res = new SynFloat(tok_.getFloatValue()); break;
        case ttIdentifier:
            return parseIdentifier();
        default:
            throw Errors::SyntaxError("Expected constant or identifier", tok_);
        }
        nextToken();
        return res;
    }

    SynExpression *BasicParser::parseExpression()
    {
        return internalParseExpression(Int2Type<0>());
    }
}
namespace __InitializeBasicParser
{
    void initialize()
    {
        using namespace CCompiler;
        OpGroups[0].insert(ttOr); // logical or expression
        OpGroups[1].insert(ttAnd); // logical and expression
        OpGroups[2].insert(ttBOr); // inclusive or expression
        OpGroups[3].insert(ttXor); // exclusive or expression
        OpGroups[4].insert(ttBAnd); // and expression
        OpGroups[5].insert(ttEqual); // equality expression
        OpGroups[5].insert(ttNotEqual);
        OpGroups[6].insert(ttLess); // relational expression
        OpGroups[6].insert(ttLessEqual);
        OpGroups[6].insert(ttGreater);
        OpGroups[6].insert(ttGreaterEqual);
        OpGroups[7].insert(ttShL); // shift expression
        OpGroups[7].insert(ttShR);
        OpGroups[8].insert(ttPlus); // additive expression
        OpGroups[8].insert(ttMinus);
        OpGroups[9].insert(ttAsterisk); // multiplicative expression
        OpGroups[9].insert(ttDiv);
        OpGroups[9].insert(ttMod);
        OpGroups[lowest_level].insert(ttInteger);
        OpGroups[lowest_level].insert(ttReal);
        OpGroups[lowest_level].insert(ttString);
        OpGroups[lowest_level].insert(ttChar);
        OpGroups[lowest_level].insert(ttIdentifier);
        AssignOps.insert(ttAssign);
        AssignOps.insert(ttMulAssign);
        AssignOps.insert(ttDivAssign);
        AssignOps.insert(ttModAssign);
        AssignOps.insert(ttPlusAssign);
        AssignOps.insert(ttMinusAssign);
        AssignOps.insert(ttShRAssign);
        AssignOps.insert(ttShLAssign);
        AssignOps.insert(ttBOrAssign);
        AssignOps.insert(ttBAndAssign);
        AssignOps.insert(ttXorAssign);
        AssignOp2BinOp["*="] = "*";
        AssignOp2BinOp["/="] = "/";
        AssignOp2BinOp["%="] = "%";
        AssignOp2BinOp["+="] = "+";
        AssignOp2BinOp["-="] = "-";
        AssignOp2BinOp["<<="] = "<<";
        AssignOp2BinOp[">>="] = ">>";
        AssignOp2BinOp["|="] = "|";
        AssignOp2BinOp["&="] = "&";
        AssignOp2BinOp["^="] = "^";
        UnaryOps.insert(ttInc);
        UnaryOps.insert(ttDecr);
        UnaryOps.insert(ttAsterisk);
        UnaryOps.insert(ttBAnd);
        UnaryOps.insert(ttPlus);
        UnaryOps.insert(ttMinus);
        UnaryOps.insert(ttNot);
        UnaryOps.insert(ttComplement);
    }
}
